{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Item Response Theory with Expectation Maximization Optimization (EMIRT)\n",
    "\n",
    "This notebook will show you how to train and use the EMIRT.\n",
    "First, we will show how to get the data (here we use a0910 as the dataset).\n",
    "Then we will show how to train a EMIRT and perform the parameters persistence.\n",
    "At last, we will show how to load the parameters from the file and evaluate on the test dataset.\n",
    "\n",
    "The script version could be found in [IRT.py](IRT.py)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user_id</th>\n",
       "      <th>item_id</th>\n",
       "      <th>score</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1615</td>\n",
       "      <td>12977</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>782</td>\n",
       "      <td>13124</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1084</td>\n",
       "      <td>16475</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>593</td>\n",
       "      <td>8690</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>127</td>\n",
       "      <td>14225</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   user_id  item_id  score\n",
       "0     1615    12977      1\n",
       "1      782    13124      0\n",
       "2     1084    16475      0\n",
       "3      593     8690      0\n",
       "4      127    14225      1"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Load the data from files\n",
    "import pandas as pd\n",
    "\n",
    "train_data = pd.read_csv(\"../../../data/a0910/train.csv\")\n",
    "valid_data = pd.read_csv(\"../../../data/a0910/valid.csv\")\n",
    "test_data = pd.read_csv(\"../../../data/a0910/test.csv\")\n",
    "\n",
    "train_data.head(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(186049, 25606, 55760)"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(train_data), len(valid_data), len(test_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4128 17746\n"
     ]
    }
   ],
   "source": [
    "stu_num = max(max(train_data['user_id']), max(test_data['user_id']))\n",
    "prob_num = max(max(train_data['item_id']), max(test_data['item_id']))\n",
    "print(stu_num, prob_num)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "R = -1 * np.ones(shape=(stu_num, prob_num))\n",
    "R[train_data['user_id']-1, train_data['item_id']-1] = train_data['score']\n",
    "\n",
    "test_set = []\n",
    "for i in range(len(test_data)):\n",
    "    row = test_data.iloc[i]\n",
    "    test_set.append({'user_id':int(row['user_id'])-1, 'item_id':int(row['item_id'])-1, 'score':row['score']})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Training and Persistence"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import logging\n",
    "logging.getLogger().setLevel(logging.INFO)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:root:save parameters to irt.params\n"
     ]
    }
   ],
   "source": [
    "from EduCDM import EMIRT\n",
    "\n",
    "cdm = EMIRT(R, stu_num, prob_num, dim=1, skip_value=-1)\n",
    "\n",
    "cdm.train(lr=1e-3, epoch=2)\n",
    "cdm.save(\"irt.params\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Loading and Testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:root:load parameters from irt.params\n",
      "evaluating: 100%|█████████████████████████████████████████████████████████████| 55760/55760 [00:00<00:00, 61866.03it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RMSE, MAE are 0.452991, 0.384722\n"
     ]
    }
   ],
   "source": [
    "cdm.load(\"irt.params\")\n",
    "rmse, mae = cdm.eval(test_set)\n",
    "print(\"RMSE, MAE are %.6f, %.6f\" % (rmse, mae))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Incremental Training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_data = [{'user_id': 0, 'item_id': 2, 'score': 0.0}, {'user_id': 1, 'item_id': 1, 'score': 1.0}]\n",
    "cdm.inc_train(new_data, lr=1e-3, epoch=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluate User's State"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "user's state is [[73.94697385]]\n"
     ]
    }
   ],
   "source": [
    "stu_rec = np.random.randint(-1, 2, size=prob_num)\n",
    "dia_state = cdm.transform(stu_rec)  # shape = (stu_num, dim)\n",
    "print(\"user's state is \" + str(dia_state))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
